package router;

import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import org.jnetpcap.Pcap;
import org.jnetpcap.PcapIf;
import org.jnetpcap.packet.JRegistry;
import org.jnetpcap.packet.PcapPacket;
import org.jnetpcap.packet.PcapPacketHandler;
import org.jnetpcap.protocol.network.Ip4;

import events.EventManager;
import events.ReceivedEvent;

import packets.OSPF;
import packets.OSPFAcknowledgement;
import packets.OSPFDescription;
import packets.OSPFHello;
import packets.OSPFRequest;
import packets.OSPFUpdate;

/**
 * Diese Klasse stellt die Schnittstelle des Routers zur Hardware dar und empfängt Pakete. 
 * @author pascal
 *
 */
public class ProtocolPacketReceiver {
	
	private PcapIf pcapInterface;
	private Interface routerInterface;

	
	public void run(){
		
		
		
		//Parameter für die capture-session
		int snaplen = 64*1024; //Alle Packete lesen, keine Beschränkung
		int flags = Pcap.MODE_PROMISCUOUS;
		int timeout = 15 * 1000;
		StringBuilder errbuf = new StringBuilder();
		
		
		/**
		 * Innere Klasse, die einen Paket Handler implementiert. 
		 * @author pascal
		 *
		 * @param <String>
		 */
		//Innere Klasse, die das Interface PcapPacketHandler aus jNetPcap implementiert
		class OSPFPacketHandler<String> implements PcapPacketHandler<String>{

			private ProtocolPacketReceiver receiver;
			
			public OSPFPacketHandler(ProtocolPacketReceiver receiver) {
				this.receiver = receiver;
			}
			
			/**
			 * Diese Methode wird ausgeführt wenn der Receiver ein neues Paket empfängt.
			 */
			@Override
			public void nextPacket(PcapPacket packet, String user) {

				//Zuerst müssen Objekte zu allen möglichen Header erzeugt werden
				//Dies wird von jNetPcap so vorgegeben, da die header vor dem capture registriert werden müssen
				OSPF ospf = new OSPF();
				OSPFAcknowledgement ack = new OSPFAcknowledgement();
				OSPFDescription description = new OSPFDescription();
				OSPFHello hello = new OSPFHello();
				OSPFRequest request = new OSPFRequest();
				OSPFUpdate update = new OSPFUpdate();
				Ip4 ip = new Ip4();
				
				if(packet.hasHeader(ip)){
					System.out.println("IP4 Paket empfangen!");
					System.out.println(packet.getState().toDebugString()); 
					System.out.println(JRegistry.toDebugString());
				}
				
				//Prüfe auf Acknowledgement
				if(packet.hasHeader(ack)){
					try {
						System.out.println("Received Acknowledgement!");
						EventManager.getInstance().notifyPacketReceived(new ReceivedEvent(this.receiver, packet.getHeader(ack)));
					} catch (UnknownHostException e) {
						e.printStackTrace();
					}
				}
				
				if(packet.hasHeader(description)){
					try {
						System.out.println("Received Description!");
						EventManager.getInstance().notifyPacketReceived(new ReceivedEvent(this.receiver, packet.getHeader(description)));
					} catch (UnknownHostException e) {
						e.printStackTrace();
					}
				}
				
				if(packet.hasHeader(hello)){
					try {
						System.out.println("Received Hello!");
						EventManager.getInstance().notifyPacketReceived(new ReceivedEvent(this.receiver, packet.getHeader(hello)));
					} catch (UnknownHostException e) {
						e.printStackTrace();
					}
				}
				
				
			}
			
		}
		
		
		OSPFPacketHandler packetHandler = new OSPFPacketHandler(this);
		
		
		//live capture öffnen
		Pcap pcap = Pcap.openLive(pcapInterface.getName(), snaplen, flags, timeout, errbuf);
		
		//100 Pakete empfangen, mit diesen wird gemäß dem packetHandler verfahren
		pcap.loop(5, packetHandler, "works");
		
		pcap.close();
		System.out.println("Connection closed!");
		
	}
	
	public ProtocolPacketReceiver(Interface routerInterface){
		
		NetworkInterface netInterface = routerInterface.getNetworkInterface();
		List<PcapIf> alldevs = new ArrayList<PcapIf>();
		StringBuilder errbuf = new StringBuilder(); 
		
		//Alle Geräte finden
		Pcap.findAllDevs(alldevs, errbuf);
		
		
		
		for(PcapIf interf : alldevs){
						
			if(interf.getName().contains(netInterface.getDisplayName())){
				
				this.pcapInterface = interf;
			}
		}
		
		System.out.println("Pcap Interface arbeitet auf: " + pcapInterface.getName());
		
	}
	
	public Interface getRouterInterface(){
		return this.routerInterface;
	}
	
	public PcapIf getPcapInterface(){
		return this.pcapInterface;
	}

}
